#!/usr/bin/perl
use strict;
use warnings;

use lib '../lib';
use lib '../extlib';
use ChessLike::Action;

use Test::Exception;
use Test::More 'no_plan';

####################
### Basic Access ###
####################

sub validate_pack_and_unpack {
   my ( $action_string, @contents ) = @_;
   my $action = ChessLike::Action->new( 'string' => $action_string );

   is( $action->tile(),
      $contents[0], "[new] $action_string: tile" );
   is( $action->index(),
      $contents[1], "[new] $action_string: index" );
   is( $action->type(),
      $contents[2], "[new] $action_string: type" );
   is( $action->target_tile(),
      $contents[3], "[new] $action_string: target-tile" );
   is( $action->target_index(),
      $contents[4], "[new] $action_string: target-index" );
   is( $action->direction(),
      $contents[5], "[new] $action_string: direction" );

   my @links = $action->links();
   is( @links, @{ $contents[6] }, "[new] $action_string: links" );

   # Check all linked game pieces
   for ( my $i = 0; $i <= @links; $i++ ) {
      is($links[$i],
         $contents[6]->[$i],
         "[new] $action_string: links $i"
      );
   }
   
   # Validate string export
   my $converted_string = $action->to_string();
   is( $converted_string, $action_string, "[to_string] $action_string" );
}

my $action;

# Basic move with implicit indices
validate_pack_and_unpack( 'a1-f4', 'a1', 0, 'move', 'f4', 0, undef, [] );

# Move from explicit index
validate_pack_and_unpack( 'a1.5-f4', 'a1', 5, 'move', 'f4', 0, undef,
   [] );

# Capture with explicit indices
validate_pack_and_unpack( 'b1.1+b4.2', 'b1', 1, 'capture', 'b4', 2, undef,
   [] );

# Capture with multi-character tiles and multicharacter indices
validate_pack_and_unpack( 'ab12.144+bu42.22', 'ab12', 144, 'capture',
   'bu42', 22, undef, [] );

# Shot with explicit index
validate_pack_and_unpack( 'x4*z6.1', 'x4', 0, 'shot', 'z6', 1, undef,
   [] );

# Basic turn on the spot
validate_pack_and_unpack( 'g8>e', 'g8', 0, 'rotation', 'g8', 0, 'e',
   [] );

# Basic turn with linked game piece and implicit index
validate_pack_and_unpack( 'g8>e/f99', 'g8', 0, 'rotation', 'g8', 0, 'e',
   [ 'f99' ] );

# Basic turn with three linked, sorted gamepieces, and explicit index
validate_pack_and_unpack( 'g8>e/c4,o9.4,zz6', 'g8', 0, 'rotation', 'g8',
   0, 'e', [ 'c4', 'o9.4', 'zz6' ] );

# Same as above, but with unsorted gamepieces
$action = ChessLike::Action->new( 'string' => 'g8>e/c4,zz6,o9.4' );
is( $action->to_string(), 'g8>e/c4,o9.4,zz6',
    '[to_string] g8>c4,zz6,o9.4: links sorted' );
is( $action->to_string( 1 ), 'g8>',
    '[to_string] g8>c4,zz6,o9.4: truncated' );

# A capture that includes a rotation and three linked units
# with explicit indices
validate_pack_and_unpack( 'ab32.7+y4.87>e/c4.2,o9.4,zz6.10',
   'ab32', 7, 'capture', 'y4', 87, 'e', [ 'c4.2', 'o9.4', 'zz6.10' ] );

$action = ChessLike::Action->new(
    'tile' => 'c7',
    'index' => 0,
    'type' => 'rotation',
    'target-tile' => 'c7',
    'target-index' => 0,
    'direction' => 'e',
);

is( $action->to_string(), 'c7>e', '[to_string] c7>e' );
is( $action->to_string( 1 ), 'c7>', '[to_string] c7>e: truncated' );

######################
### Swapping Links ###
######################

$action = ChessLike::Action->new(
    'tile' => 'a2',
    'index' => 0,
    'type' => 'move',
    'target-tile' => 'b3',
    'target-index' => 0,
    'direction' => undef,
    'links' => [ 'a3.1', 'a4.0' ],
    'possible-links' => [ 'a1.4', 'b3.0' ],
);

dies_ok { $action->upgrade_link( 'c4.0' ) }
         "[upgrade_link] invalid link, dies";

$action->upgrade_link( 'a1.4' );

my @links = $action->links();
my @shared_links = $action->shared_links();
my @unshared_links = $action->unshared_links();

is( @links, 3, "[upgrade_link] links count" );
is( $links[-1], 'a1.4', "[upgrade_link] links contents" );
is( @shared_links, 2, "[upgrade_link] shared-links count" );
is( $shared_links[0], 'a1.4', "[upgrade_link] shared-links count" );
is( $shared_links[1], 'b3.0', "[upgrade_link] shared-links count" );
is( @unshared_links, 2, "[upgrade_link] unshared-links count" );
is( $unshared_links[0], 'a3.1', "[upgrade_link] unshared-links count" );
is( $unshared_links[1], 'a4.0', "[upgrade_link] unshared-links count" );

is( $action->to_string(), 'a2-b3/a1.4,a3.1,a4',
    '[to_string] a2-b3/a1.4,a3.1,a4' );
is( $action->to_string( 1 ), 'a2-b3',
    '[to_string] a2-b3/a1.4,a3.1,a4: truncated' );

dies_ok { $action->downgrade_link( 'c4.0' ) }
         "[downgrade_link] invalid link, dies";

dies_ok { $action->downgrade_link( 'a4.0' ) }
         "[downgrade_link] unshared link, dies";

$action->downgrade_link( 'a1.4' );

@links = $action->links();
@shared_links = $action->shared_links();
@unshared_links = $action->unshared_links();

is( @links, 2, "[downgrade_link] links count" );
is( $links[0], 'a3.1', "[downgrade_link] links contents 0" );
is( $links[1], 'a4.0', "[downgrade_link] links contents 1" );
is( @shared_links, 2, "[downgrade_link] shared-links count" );
is( $shared_links[0], 'a1.4',
    "[downgrade_link] shared-links contents 0" );
is( $shared_links[1], 'b3.0',
    "[downgrade_link] shared-links contents 1" );
is( @unshared_links, 2, "[downgrade_link] unshared-links count" );
is( $unshared_links[0], 'a3.1', "[downgrade_link] unshared-links count" );
is( $unshared_links[1], 'a4.0', "[downgrade_link] unshared-links count" );

is( $action->to_string(), 'a2-b3/a3.1,a4',
    '[to_string] a2-b3/a3.1,a4' );
is( $action->to_string( 1 ), 'a2-b3',
    '[to_string] a2-b3/a3.1,a4: truncated' );
